/*
    Leaflet.draw, a plugin that adds drawing and editing tools to Leaflet powered maps.
    (c) 2012-2013, Jacob Toye, Smartrak

    https://github.com/Leaflet/Leaflet.draw
    http://leafletjs.com
    https://github.com/jacobtoye
*/
(function (window, document, undefined) {/*
 * Leaflet.draw assumes that you have already included the Leaflet library.
 */

L.drawVersion = '0.2.4-dev';

L.drawLocal = {
    draw: {
        toolbar: {
            actions: {
                title: '取消绘制',
                text: '取消'
            },
            undo: {
                title: '撤销',
                text: '撤销'
            },
            buttons: {
                polyline: '绘制线路',
                polygon: '绘制多边形',
                rectangle: '绘制长方形',
                circle: '绘制圆',
                marker: '绘制一个图标'
            }
        },
        handlers: {
            circle: {
                tooltip: {
                    start: '点击和拖动来完成绘制圆.'
                }
            },
            marker: {
                tooltip: {
                    start: '点击地图来放置图标.'
                }
            },
            polygon: {
                tooltip: {
                    start: '点击来绘制图形.',
                    cont: '点击地图继续绘制.',
                    end: '点击起始点完成量测.'
                }
            },
            polyline: {
                error: '<strong>Error:</strong> shape edges cannot cross!',
                tooltip: {
                    start: '点击开始绘制.',
                    cont: '点击地图继续绘制.',
                    end: '再次点击完成量测.'
                }
            },
            rectangle: {
                tooltip: {
                    start: '点击并拖动绘制长方形.'
                }
            },
            simpleshape: {
                tooltip: {
                    end: '松开鼠标完成绘制.'
                }
            }
        }
    },
    edit: {
        toolbar: {
            actions: {
                save: {
                    title: '保存更改.',
                    text: '保存'
                },
                cancel: {
                    title: '取消编辑,放弃所有更改.',
                    text: '取消'
                }
            },
            buttons: {
                edit: '编辑图层.',
                editDisabled: '没有要编辑的图层.',
                remove: '删除图层.',
                removeDisabled: '没有要删除的图层.'
            }
        },
        handlers: {
            edit: {
                tooltip: {
                    text: '拖动节点或图标来进行编辑.',
                    subtext: '点击取消来撤掉操作.'
                }
            },
            remove: {
                tooltip: {
                    text: '点击要移除的特征物体'
                }
            }
        }
    }
};


L.Draw = {};

L.Draw.Feature = L.Handler.extend({
    includes: L.Mixin.Events,

    initialize: function (map, options) {
        this._map = map;
        this._container = map._container;
        this._overlayPane = map._panes.overlayPane;
        this._popupPane = map._panes.popupPane;

        // Merge default shapeOptions options with custom shapeOptions
        if (options && options.shapeOptions) {
            options.shapeOptions = L.Util.extend({}, this.options.shapeOptions, options.shapeOptions);
        }
        L.setOptions(this, options);
    },

    enable: function () {
        if (this._enabled) { return; }

        L.Handler.prototype.enable.call(this);

        this.fire('enabled', { handler: this.type });

        this._map.fire('draw:drawstart', { layerType: this.type });
    },

    disable: function () {
        if (!this._enabled) { return; }

        L.Handler.prototype.disable.call(this);

        this._map.fire('draw:drawstop', { layerType: this.type });

        this.fire('disabled', { handler: this.type });
    },

    addHooks: function () {
        var map = this._map;

        if (map) {
            L.DomUtil.disableTextSelection();

            map.getContainer().focus();

            this._tooltip = new L.Tooltip(this._map);

            L.DomEvent.on(this._container, 'keyup', this._cancelDrawing, this);
        }
    },

    removeHooks: function () {
        if (this._map) {
            L.DomUtil.enableTextSelection();

            this._tooltip.dispose();
            this._tooltip = null;

            L.DomEvent.off(this._container, 'keyup', this._cancelDrawing, this);
        }
    },

    setOptions: function (options) {
        L.setOptions(this, options);
    },

    _fireCreatedEvent: function (layer, info) {
        this._map.fire('draw:created', { layer: layer, layerType: this.type, layerInfo : info });
    },

    // Cancel drawing when the escape key is pressed
    _cancelDrawing: function (e) {
        if (e.keyCode === 27) {
            this.disable();
        }
    }
});

L.Draw.Polyline = L.Draw.Feature.extend({
    statics: {
        TYPE: 'polyline'
    },

    Poly: L.Polyline,

    options: {
        allowIntersection: true,
        repeatMode: false,
        drawError: {
            color: '#b00b00',
            timeout: 2500
        },
        icon: new L.DivIcon({
            iconSize: new L.Point(8, 8),
            className: 'leaflet-div-icon leaflet-editing-icon'
        }),
        guidelineDistance: 20,
        maxGuideLineLength: 4000,
        shapeOptions: {
            stroke: true,
            color: '#f06eaa',
            weight: 4,
            opacity: 0.5,
            fill: false,
            clickable: true
        },
        metric: true, // Whether to use the metric meaurement system or imperial
        showLength: true, // Whether to display distance in the tooltip
        zIndexOffset: 2000 // This should be > than the highest z-index any map layers
    },

    initialize: function (map, options) {
        // Need to set this here to ensure the correct message is used.
        this.options.drawError.message = L.drawLocal.draw.handlers.polyline.error;

        // Merge default drawError options with custom options
        if (options && options.drawError) {
            options.drawError = L.Util.extend({}, this.options.drawError, options.drawError);
        }

        // Save the type so super can fire, need to do this as cannot do this.TYPE :(
        this.type = L.Draw.Polyline.TYPE;

        L.Draw.Feature.prototype.initialize.call(this, map, options);
    },

    addHooks: function () {
        L.Draw.Feature.prototype.addHooks.call(this);
        if (this._map) {
            this._markers = [];

            this._markerGroup = new L.LayerGroup();
            this._map.addLayer(this._markerGroup);

            this._poly = new L.Polyline([], this.options.shapeOptions);

            this._tooltip.updateContent(this._getTooltipText());

            // Make a transparent marker that will used to catch click events. These click
            // events will create the vertices. We need to do this so we can ensure that
            // we can create vertices over other map layers (markers, vector layers). We
            // also do not want to trigger any click handlers of objects we are clicking on
            // while drawing.
            if (!this._mouseMarker) {
                this._mouseMarker = L.marker(this._map.getCenter(), {
                    icon: L.divIcon({
                        className: 'leaflet-mouse-marker',
                        iconAnchor: [20, 20],
                        iconSize: [40, 40]
                    }),
                    opacity: 0,
                    zIndexOffset: this.options.zIndexOffset
                });
            }

            this._mouseMarker
                .on('mousedown', this._onMouseDown, this)
                .addTo(this._map);

            this._map
                .on('mousemove', this._onMouseMove, this)
                .on('mouseup', this._onMouseUp, this)
                .on('zoomend', this._onZoomEnd, this);
        }
    },

    removeHooks: function () {
        L.Draw.Feature.prototype.removeHooks.call(this);

        this._clearHideErrorTimeout();

        this._cleanUpShape();

        // remove markers from map
        this._map.removeLayer(this._markerGroup);
        delete this._markerGroup;
        delete this._markers;

        this._map.removeLayer(this._poly);
        delete this._poly;

        this._mouseMarker
            .off('mousedown', this._onMouseDown, this)
            .off('mouseup', this._onMouseUp, this);
        this._map.removeLayer(this._mouseMarker);
        delete this._mouseMarker;

        // clean up DOM
        this._clearGuides();

        this._map
            .off('mousemove', this._onMouseMove, this)
            .off('zoomend', this._onZoomEnd, this);
    },

    deleteLastVertex: function () {
        if (this._markers.length <= 1) {
            return;
        }

        var lastMarker = this._markers.pop(),
            poly = this._poly,
            latlng = this._poly.spliceLatLngs(poly.getLatLngs().length - 1, 1)[0];

        this._markerGroup.removeLayer(lastMarker);

        if (poly.getLatLngs().length < 2) {
            this._map.removeLayer(poly);
        }

        this._vertexChanged(latlng, false);
    },

    addVertex: function (latlng) {
        var markersLength = this._markers.length;

        if (markersLength > 0 && !this.options.allowIntersection && this._poly.newLatLngIntersects(latlng)) {
            this._showErrorTooltip();
            return;
        }
        else if (this._errorShown) {
            this._hideErrorTooltip();
        }

        this._markers.push(this._createMarker(latlng));

        this._poly.addLatLng(latlng);

        if (this._poly.getLatLngs().length === 2) {
            this._map.addLayer(this._poly);
        }

        this._vertexChanged(latlng, true);
    },

    _finishShape: function () {
        var intersects = this._poly.newLatLngIntersects(this._poly.getLatLngs()[0], true);

        if ((!this.options.allowIntersection && intersects) || !this._shapeIsValid()) {
            this._showErrorTooltip();
            return;
        }

        this._fireCreatedEvent(this);
        this.disable();
        if (this.options.repeatMode) {
            this.enable();
        }
    },

    //Called to verify the shape is valid when the user tries to finish it
    //Return false if the shape is not valid
    _shapeIsValid: function () {
        return true;
    },

    _onZoomEnd: function () {
        this._updateGuide();
    },

    _onMouseMove: function (e) {
        var newPos = e.layerPoint,
            latlng = e.latlng;

        // Save latlng
        // should this be moved to _updateGuide() ?
        this._currentLatLng = latlng;

        this._updateTooltip(latlng);

        // Update the guide line
        this._updateGuide(newPos);

        // Update the mouse marker position
        this._mouseMarker.setLatLng(latlng);

        L.DomEvent.preventDefault(e.originalEvent);
    },

    _vertexChanged: function (latlng, added) {
        this._updateFinishHandler();

        this._updateRunningMeasure(latlng, added);

        this._clearGuides();

        this._updateTooltip();
    },

    _onMouseDown: function (e) {
        var originalEvent = e.originalEvent;
        this._mouseDownOrigin = L.point(originalEvent.clientX, originalEvent.clientY);
    },

    _onMouseUp: function (e) {
        if (this._mouseDownOrigin) {
            // We detect clicks within a certain tolerance, otherwise let it
            // be interpreted as a drag by the map
            var distance = L.point(e.originalEvent.clientX, e.originalEvent.clientY)
                .distanceTo(this._mouseDownOrigin);
            if (Math.abs(distance) < 9 * (window.devicePixelRatio || 1)) {
                this.addVertex(e.latlng);
            }
        }
        this._mouseDownOrigin = null;
    },

    _updateFinishHandler: function () {
        var markerCount = this._markers.length;
        // The last marker should have a click handler to close the polyline
        if (markerCount > 1) {
            this._markers[markerCount - 1].on('click', this._finishShape, this);
        }

        // Remove the old marker click handler (as only the last point should close the polyline)
        if (markerCount > 2) {
            this._markers[markerCount - 2].off('click', this._finishShape, this);
        }
    },

    _createMarker: function (latlng) {
        var marker = new L.Marker(latlng, {
            icon: this.options.icon,
            zIndexOffset: this.options.zIndexOffset * 2
        });

        this._markerGroup.addLayer(marker);

        return marker;
    },

    _updateGuide: function (newPos) {
        var markerCount = this._markers.length;

        if (markerCount > 0) {
            newPos = newPos || this._map.latLngToLayerPoint(this._currentLatLng);

            // draw the guide line
            this._clearGuides();
            this._drawGuide(
                this._map.latLngToLayerPoint(this._markers[markerCount - 1].getLatLng()),
                newPos
            );
        }
    },

    _updateTooltip: function (latLng) {
        var text = this._getTooltipText();

        if (latLng) {
            this._tooltip.updatePosition(latLng);
        }

        if (!this._errorShown) {
            this._tooltip.updateContent(text);
        }
    },

    _drawGuide: function (pointA, pointB) {
        var length = Math.floor(Math.sqrt(Math.pow((pointB.x - pointA.x), 2) + Math.pow((pointB.y - pointA.y), 2))),
            guidelineDistance = this.options.guidelineDistance,
            maxGuideLineLength = this.options.maxGuideLineLength,
            // Only draw a guideline with a max length
            i = length > maxGuideLineLength ? length - maxGuideLineLength : guidelineDistance,
            fraction,
            dashPoint,
            dash;

        //create the guides container if we haven't yet
        if (!this._guidesContainer) {
            this._guidesContainer = L.DomUtil.create('div', 'leaflet-draw-guides', this._overlayPane);
        }

        //draw a dash every GuildeLineDistance
        for (; i < length; i += this.options.guidelineDistance) {
            //work out fraction along line we are
            fraction = i / length;

            //calculate new x,y point
            dashPoint = {
                x: Math.floor((pointA.x * (1 - fraction)) + (fraction * pointB.x)),
                y: Math.floor((pointA.y * (1 - fraction)) + (fraction * pointB.y))
            };

            //add guide dash to guide container
            dash = L.DomUtil.create('div', 'leaflet-draw-guide-dash', this._guidesContainer);
            dash.style.backgroundColor =
                !this._errorShown ? this.options.shapeOptions.color : this.options.drawError.color;

            L.DomUtil.setPosition(dash, dashPoint);
        }
    },

    _updateGuideColor: function (color) {
        if (this._guidesContainer) {
            for (var i = 0, l = this._guidesContainer.childNodes.length; i < l; i++) {
                this._guidesContainer.childNodes[i].style.backgroundColor = color;
            }
        }
    },

    // removes all child elements (guide dashes) from the guides container
    _clearGuides: function () {
        if (this._guidesContainer) {
            while (this._guidesContainer.firstChild) {
                this._guidesContainer.removeChild(this._guidesContainer.firstChild);
            }
        }
    },

    _getTooltipText: function () {
        var showLength = this.options.showLength,
            labelText, distanceStr;

        if (this._markers.length === 0) {
            labelText = {
                text: L.drawLocal.draw.handlers.polyline.tooltip.start
            };
        } else {
            distanceStr = showLength ? this._getMeasurementString() : '';

            if (this._markers.length === 1) {
                labelText = {
                    text: L.drawLocal.draw.handlers.polyline.tooltip.cont,
                    subtext: distanceStr
                };
            } else {
                labelText = {
                    text: L.drawLocal.draw.handlers.polyline.tooltip.end,
                    subtext: distanceStr
                };
            }
        }
        return labelText;
    },

    _updateRunningMeasure: function (latlng, added) {
        var markersLength = this._markers.length,
            previousMarkerIndex, distance;

        if (this._markers.length === 1) {
            this._measurementRunningTotal = 0;
        } else {
            previousMarkerIndex = markersLength - (added ? 2 : 1);
            distance = latlng.distanceTo(this._markers[previousMarkerIndex].getLatLng());

            this._measurementRunningTotal += distance * (added ? 1 : -1);
        }
    },

    _getMeasurementString: function () {
        var currentLatLng = this._currentLatLng,
            previousLatLng = this._markers[this._markers.length - 1].getLatLng(),
            distance;

        // calculate the distance from the last fixed point to the mouse position
        distance = this._measurementRunningTotal + currentLatLng.distanceTo(previousLatLng);

        return L.GeometryUtil.readableDistance(distance, this.options.metric);
    },

    _showErrorTooltip: function () {
        this._errorShown = true;

        // Update tooltip
        this._tooltip
            .showAsError()
            .updateContent({ text: this.options.drawError.message });

        // Update shape
        this._updateGuideColor(this.options.drawError.color);
        this._poly.setStyle({ color: this.options.drawError.color });

        // Hide the error after 2 seconds
        this._clearHideErrorTimeout();
        this._hideErrorTimeout = setTimeout(L.Util.bind(this._hideErrorTooltip, this), this.options.drawError.timeout);
    },

    _hideErrorTooltip: function () {
        this._errorShown = false;

        this._clearHideErrorTimeout();

        // Revert tooltip
        this._tooltip
            .removeError()
            .updateContent(this._getTooltipText());

        // Revert shape
        this._updateGuideColor(this.options.shapeOptions.color);
        this._poly.setStyle({ color: this.options.shapeOptions.color });
    },

    _clearHideErrorTimeout: function () {
        if (this._hideErrorTimeout) {
            clearTimeout(this._hideErrorTimeout);
            this._hideErrorTimeout = null;
        }
    },

    _cleanUpShape: function () {
        if (this._markers.length > 1) {
            this._markers[this._markers.length - 1].off('click', this._finishShape, this);
        }
    },

    _fireCreatedEvent: function () {
        var poly = new this.Poly(this._poly.getLatLngs(), this.options.shapeOptions);
        L.Draw.Feature.prototype._fireCreatedEvent.call(this, poly, this._getMeasurementString());
    }
});


L.Draw.Polygon = L.Draw.Polyline.extend({
    statics: {
        TYPE: 'polygon'
    },

    Poly: L.Polygon,

    options: {
        showArea: false,
        shapeOptions: {
            stroke: true,
            color: '#f06eaa',
            weight: 4,
            opacity: 0.5,
            fill: true,
            fillColor: null, //same as color by default
            fillOpacity: 0.2,
            clickable: true
        }
    },

    initialize: function (map, options) {
        L.Draw.Polyline.prototype.initialize.call(this, map, options);

        // Save the type so super can fire, need to do this as cannot do this.TYPE :(
        this.type = L.Draw.Polygon.TYPE;
    },

    _updateFinishHandler: function () {
        var markerCount = this._markers.length;

        // The first marker should have a click handler to close the polygon
        if (markerCount === 1) {
            this._markers[0].on('click', this._finishShape, this);
        }

        // Add and update the double click handler
        if (markerCount > 2) {
            this._markers[markerCount - 1].on('dblclick', this._finishShape, this);
            // Only need to remove handler if has been added before
            if (markerCount > 3) {
                this._markers[markerCount - 2].off('dblclick', this._finishShape, this);
            }
        }
    },

    _getTooltipText: function () {
        var text, subtext;

        if (this._markers.length === 0) {
            text = L.drawLocal.draw.handlers.polygon.tooltip.start;
        } else if (this._markers.length < 3) {
            text = L.drawLocal.draw.handlers.polygon.tooltip.cont;
        } else {
            text = L.drawLocal.draw.handlers.polygon.tooltip.end;
            subtext = this._getMeasurementString();
        }

        return {
            text: text,
            subtext: subtext
        };
    },

    _getMeasurementString: function () {
        var area = this._area;

        if (!area) {
            return null;
        }

        return L.GeometryUtil.readableArea(area, this.options.metric);
    },

    _shapeIsValid: function () {
        return this._markers.length >= 3;
    },

    _vertexChanged: function (latlng, added) {
        var latLngs;

        // Check to see if we should show the area
        if (!this.options.allowIntersection && this.options.showArea) {
            latLngs = this._poly.getLatLngs();

            this._area = L.GeometryUtil.geodesicArea(latLngs);
        }

        L.Draw.Polyline.prototype._vertexChanged.call(this, latlng, added);
    },

    _cleanUpShape: function () {
        var markerCount = this._markers.length;

        if (markerCount > 0) {
            this._markers[0].off('click', this._finishShape, this);

            if (markerCount > 2) {
                this._markers[markerCount - 1].off('dblclick', this._finishShape, this);
            }
        }
    }
});


L.SimpleShape = {};

L.Draw.SimpleShape = L.Draw.Feature.extend({
    options: {
        repeatMode: false
    },

    initialize: function (map, options) {
        this._endLabelText = L.drawLocal.draw.handlers.simpleshape.tooltip.end;

        L.Draw.Feature.prototype.initialize.call(this, map, options);
    },

    addHooks: function () {
        L.Draw.Feature.prototype.addHooks.call(this);
        if (this._map) {
            this._mapDraggable = this._map.dragging.enabled();

            if (this._mapDraggable) {
                this._map.dragging.disable();
            }

            //TODO refactor: move cursor to styles
            this._container.style.cursor = 'crosshair';

            this._tooltip.updateContent({ text: this._initialLabelText });

            this._map
                .on('mousedown', this._onMouseDown, this)
                .on('mousemove', this._onMouseMove, this);
        }
    },

    removeHooks: function () {
        L.Draw.Feature.prototype.removeHooks.call(this);
        if (this._map) {
            if (this._mapDraggable) {
                this._map.dragging.enable();
            }

            //TODO refactor: move cursor to styles
            this._container.style.cursor = '';

            this._map
                .off('mousedown', this._onMouseDown, this)
                .off('mousemove', this._onMouseMove, this);

            L.DomEvent.off(document, 'mouseup', this._onMouseUp, this);

            // If the box element doesn't exist they must not have moved the mouse, so don't need to destroy/return
            if (this._shape) {
                this._map.removeLayer(this._shape);
                delete this._shape;
            }
        }
        this._isDrawing = false;
    },

    _onMouseDown: function (e) {
        this._isDrawing = true;
        this._startLatLng = e.latlng;

        L.DomEvent
            .on(document, 'mouseup', this._onMouseUp, this)
            .preventDefault(e.originalEvent);
    },

    _onMouseMove: function (e) {
        var latlng = e.latlng;

        this._tooltip.updatePosition(latlng);
        if (this._isDrawing) {
            this._tooltip.updateContent({ text: this._endLabelText });
            this._drawShape(latlng);
        }
    },

    _onMouseUp: function () {
        if (this._shape) {
            this._fireCreatedEvent();
        }

        this.disable();
        if (this.options.repeatMode) {
            this.enable();
        }
    }
});

L.Draw.Rectangle = L.Draw.SimpleShape.extend({
    statics: {
        TYPE: 'rectangle'
    },

    options: {
        shapeOptions: {
            stroke: true,
            color: '#f06eaa',
            weight: 4,
            opacity: 0.5,
            fill: true,
            fillColor: null, //same as color by default
            fillOpacity: 0.2,
            clickable: true
        }
    },

    initialize: function (map, options) {
        // Save the type so super can fire, need to do this as cannot do this.TYPE :(
        this.type = L.Draw.Rectangle.TYPE;

        this._initialLabelText = L.drawLocal.draw.handlers.rectangle.tooltip.start;

        L.Draw.SimpleShape.prototype.initialize.call(this, map, options);
    },

    _drawShape: function (latlng) {
        if (!this._shape) {
            this._shape = new L.Rectangle(new L.LatLngBounds(this._startLatLng, latlng), this.options.shapeOptions);
            this._map.addLayer(this._shape);
        } else {
            this._shape.setBounds(new L.LatLngBounds(this._startLatLng, latlng));
        }
    },

    _fireCreatedEvent: function () {
        var rectangle = new L.Rectangle(this._shape.getBounds(), this.options.shapeOptions);
        L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, rectangle);
    }
});


L.Draw.Circle = L.Draw.SimpleShape.extend({
    statics: {
        TYPE: 'circle'
    },

    options: {
        shapeOptions: {
            stroke: true,
            color: '#f06eaa',
            weight: 4,
            opacity: 0.5,
            fill: true,
            fillColor: null, //same as color by default
            fillOpacity: 0.2,
            clickable: true
        },
        showRadius: true,
        metric: true // Whether to use the metric meaurement system or imperial
    },

    initialize: function (map, options) {
        // Save the type so super can fire, need to do this as cannot do this.TYPE :(
        this.type = L.Draw.Circle.TYPE;

        this._initialLabelText = L.drawLocal.draw.handlers.circle.tooltip.start;

        L.Draw.SimpleShape.prototype.initialize.call(this, map, options);
    },

    _drawShape: function (latlng) {
        if (!this._shape) {
            this._shape = new L.Circle(this._startLatLng, this._startLatLng.distanceTo(latlng), this.options.shapeOptions);
            this._map.addLayer(this._shape);
        } else {
            this._shape.setRadius(this._startLatLng.distanceTo(latlng));
        }
    },

    _fireCreatedEvent: function () {
        var circle = new L.Circle(this._startLatLng, this._shape.getRadius(), this.options.shapeOptions);
        L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, circle);
    },

    _onMouseMove: function (e) {
        var latlng = e.latlng,
            showRadius = this.options.showRadius,
            useMetric = this.options.metric,
            radius;

        this._tooltip.updatePosition(latlng);
        if (this._isDrawing) {
            this._drawShape(latlng);

            // Get the new radius (rounded to 1 dp)
            radius = this._shape.getRadius().toFixed(1);

            this._tooltip.updateContent({
                text: this._endLabelText,
                subtext: showRadius ? 'Radius: ' + L.GeometryUtil.readableDistance(radius, useMetric) : ''
            });
        }
    }
});


L.Draw.Marker = L.Draw.Feature.extend({
    statics: {
        TYPE: 'marker'
    },

    options: {
        icon: new L.Icon.Default(),
        repeatMode: false,
        zIndexOffset: 2000 // This should be > than the highest z-index any markers
    },

    initialize: function (map, options) {
        // Save the type so super can fire, need to do this as cannot do this.TYPE :(
        this.type = L.Draw.Marker.TYPE;

        L.Draw.Feature.prototype.initialize.call(this, map, options);
    },

    addHooks: function () {
        L.Draw.Feature.prototype.addHooks.call(this);

        if (this._map) {
            this._tooltip.updateContent({ text: L.drawLocal.draw.handlers.marker.tooltip.start });

            // Same mouseMarker as in Draw.Polyline
            if (!this._mouseMarker) {
                this._mouseMarker = L.marker(this._map.getCenter(), {
                    icon: L.divIcon({
                        className: 'leaflet-mouse-marker',
                        iconAnchor: [20, 20],
                        iconSize: [40, 40]
                    }),
                    opacity: 0,
                    zIndexOffset: this.options.zIndexOffset
                });
            }

            this._mouseMarker
                .on('click', this._onClick, this)
                .addTo(this._map);

            this._map.on('mousemove', this._onMouseMove, this);
        }
    },

    removeHooks: function () {
        L.Draw.Feature.prototype.removeHooks.call(this);

        if (this._map) {
            if (this._marker) {
                this._marker.off('click', this._onClick, this);
                this._map
                    .off('click', this._onClick, this)
                    .removeLayer(this._marker);
                delete this._marker;
            }

            this._mouseMarker.off('click', this._onClick, this);
            this._map.removeLayer(this._mouseMarker);
            delete this._mouseMarker;

            this._map.off('mousemove', this._onMouseMove, this);
        }
    },

    _onMouseMove: function (e) {
        var latlng = e.latlng;

        this._tooltip.updatePosition(latlng);
        this._mouseMarker.setLatLng(latlng);

        if (!this._marker) {
            this._marker = new L.Marker(latlng, {
                icon: this.options.icon,
                zIndexOffset: this.options.zIndexOffset
            });
            // Bind to both marker and map to make sure we get the click event.
            this._marker.on('click', this._onClick, this);
            this._map
                .on('click', this._onClick, this)
                .addLayer(this._marker);
        }
        else {
            latlng = this._mouseMarker.getLatLng();
            this._marker.setLatLng(latlng);
        }
    },

    _onClick: function () {
        this._fireCreatedEvent();

        this.disable();
        if (this.options.repeatMode) {
            this.enable();
        }
    },

    _fireCreatedEvent: function () {
        var marker = new L.Marker(this._marker.getLatLng(), { icon: this.options.icon });
        L.Draw.Feature.prototype._fireCreatedEvent.call(this, marker);
    }
});


L.Edit = L.Edit || {};

/*
 * L.Edit.Poly is an editing handler for polylines and polygons.
 */

L.Edit.Poly = L.Handler.extend({
    options: {
        icon: new L.DivIcon({
            iconSize: new L.Point(8, 8),
            className: 'leaflet-div-icon leaflet-editing-icon'
        })
    },

    initialize: function (poly, options) {
        this._poly = poly;
        L.setOptions(this, options);
    },

    addHooks: function () {
        if (this._poly._map) {
            if (!this._markerGroup) {
                this._initMarkers();
            }
            this._poly._map.addLayer(this._markerGroup);
        }
    },

    removeHooks: function () {
        if (this._poly._map) {
            this._poly._map.removeLayer(this._markerGroup);
            delete this._markerGroup;
            delete this._markers;
        }
    },

    updateMarkers: function () {
        this._markerGroup.clearLayers();
        this._initMarkers();
    },

    _initMarkers: function () {
        if (!this._markerGroup) {
            this._markerGroup = new L.LayerGroup();
        }
        this._markers = [];

        var latlngs = this._poly._latlngs,
            i, j, len, marker;

        // TODO refactor holes implementation in Polygon to support it here

        for (i = 0, len = latlngs.length; i < len; i++) {

            marker = this._createMarker(latlngs[i], i);
            marker.on('click', this._onMarkerClick, this);
            this._markers.push(marker);
        }

        var markerLeft, markerRight;

        for (i = 0, j = len - 1; i < len; j = i++) {
            if (i === 0 && !(L.Polygon && (this._poly instanceof L.Polygon))) {
                continue;
            }

            markerLeft = this._markers[j];
            markerRight = this._markers[i];

            this._createMiddleMarker(markerLeft, markerRight);
            this._updatePrevNext(markerLeft, markerRight);
        }
    },

    _createMarker: function (latlng, index) {
        var marker = new L.Marker(latlng, {
            draggable: true,
            icon: this.options.icon
        });

        marker._origLatLng = latlng;
        marker._index = index;

        marker.on('drag', this._onMarkerDrag, this);
        marker.on('dragend', this._fireEdit, this);

        this._markerGroup.addLayer(marker);

        return marker;
    },

    _removeMarker: function (marker) {
        var i = marker._index;

        this._markerGroup.removeLayer(marker);
        this._markers.splice(i, 1);
        this._poly.spliceLatLngs(i, 1);
        this._updateIndexes(i, -1);

        marker
            .off('drag', this._onMarkerDrag, this)
            .off('dragend', this._fireEdit, this)
            .off('click', this._onMarkerClick, this);
    },

    _fireEdit: function () {
        this._poly.edited = true;
        this._poly.fire('edit');
    },

    _onMarkerDrag: function (e) {
        var marker = e.target;

        L.extend(marker._origLatLng, marker._latlng);

        if (marker._middleLeft) {
            marker._middleLeft.setLatLng(this._getMiddleLatLng(marker._prev, marker));
        }
        if (marker._middleRight) {
            marker._middleRight.setLatLng(this._getMiddleLatLng(marker, marker._next));
        }

        this._poly.redraw();
    },

    _onMarkerClick: function (e) {
        var minPoints = L.Polygon && (this._poly instanceof L.Polygon) ? 4 : 3,
            marker = e.target;

        // If removing this point would create an invalid polyline/polygon don't remove
        if (this._poly._latlngs.length < minPoints) {
            return;
        }

        // remove the marker
        this._removeMarker(marker);

        // update prev/next links of adjacent markers
        this._updatePrevNext(marker._prev, marker._next);

        // remove ghost markers near the removed marker
        if (marker._middleLeft) {
            this._markerGroup.removeLayer(marker._middleLeft);
        }
        if (marker._middleRight) {
            this._markerGroup.removeLayer(marker._middleRight);
        }

        // create a ghost marker in place of the removed one
        if (marker._prev && marker._next) {
            this._createMiddleMarker(marker._prev, marker._next);

        } else if (!marker._prev) {
            marker._next._middleLeft = null;

        } else if (!marker._next) {
            marker._prev._middleRight = null;
        }

        this._fireEdit();
    },

    _updateIndexes: function (index, delta) {
        this._markerGroup.eachLayer(function (marker) {
            if (marker._index > index) {
                marker._index += delta;
            }
        });
    },

    _createMiddleMarker: function (marker1, marker2) {
        var latlng = this._getMiddleLatLng(marker1, marker2),
            marker = this._createMarker(latlng),
            onClick,
            onDragStart,
            onDragEnd;

        marker.setOpacity(0.6);

        marker1._middleRight = marker2._middleLeft = marker;

        onDragStart = function () {
            var i = marker2._index;

            marker._index = i;

            marker
                .off('click', onClick, this)
                .on('click', this._onMarkerClick, this);

            latlng.lat = marker.getLatLng().lat;
            latlng.lng = marker.getLatLng().lng;
            this._poly.spliceLatLngs(i, 0, latlng);
            this._markers.splice(i, 0, marker);

            marker.setOpacity(1);

            this._updateIndexes(i, 1);
            marker2._index++;
            this._updatePrevNext(marker1, marker);
            this._updatePrevNext(marker, marker2);

            this._poly.fire('editstart');
        };

        onDragEnd = function () {
            marker.off('dragstart', onDragStart, this);
            marker.off('dragend', onDragEnd, this);

            this._createMiddleMarker(marker1, marker);
            this._createMiddleMarker(marker, marker2);
        };

        onClick = function () {
            onDragStart.call(this);
            onDragEnd.call(this);
            this._fireEdit();
        };

        marker
            .on('click', onClick, this)
            .on('dragstart', onDragStart, this)
            .on('dragend', onDragEnd, this);

        this._markerGroup.addLayer(marker);
    },

    _updatePrevNext: function (marker1, marker2) {
        if (marker1) {
            marker1._next = marker2;
        }
        if (marker2) {
            marker2._prev = marker1;
        }
    },

    _getMiddleLatLng: function (marker1, marker2) {
        var map = this._poly._map,
            p1 = map.project(marker1.getLatLng()),
            p2 = map.project(marker2.getLatLng());

        return map.unproject(p1._add(p2)._divideBy(2));
    }
});

L.Polyline.addInitHook(function () {

    // Check to see if handler has already been initialized. This is to support versions of Leaflet that still have L.Handler.PolyEdit
    if (this.editing) {
        return;
    }

    if (L.Edit.Poly) {
        this.editing = new L.Edit.Poly(this);

        if (this.options.editable) {
            this.editing.enable();
        }
    }

    this.on('add', function () {
        if (this.editing && this.editing.enabled()) {
            this.editing.addHooks();
        }
    });

    this.on('remove', function () {
        if (this.editing && this.editing.enabled()) {
            this.editing.removeHooks();
        }
    });
});


L.Edit = L.Edit || {};

L.Edit.SimpleShape = L.Handler.extend({
    options: {
        moveIcon: new L.DivIcon({
            iconSize: new L.Point(8, 8),
            className: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-move'
        }),
        resizeIcon: new L.DivIcon({
            iconSize: new L.Point(8, 8),
            className: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-resize'
        })
    },

    initialize: function (shape, options) {
        this._shape = shape;
        L.Util.setOptions(this, options);
    },

    addHooks: function () {
        if (this._shape._map) {
            this._map = this._shape._map;

            if (!this._markerGroup) {
                this._initMarkers();
            }
            this._map.addLayer(this._markerGroup);
        }
    },

    removeHooks: function () {
        if (this._shape._map) {
            this._unbindMarker(this._moveMarker);

            for (var i = 0, l = this._resizeMarkers.length; i < l; i++) {
                this._unbindMarker(this._resizeMarkers[i]);
            }
            this._resizeMarkers = null;

            this._map.removeLayer(this._markerGroup);
            delete this._markerGroup;
        }

        this._map = null;
    },

    updateMarkers: function () {
        this._markerGroup.clearLayers();
        this._initMarkers();
    },

    _initMarkers: function () {
        if (!this._markerGroup) {
            this._markerGroup = new L.LayerGroup();
        }

        // Create center marker
        this._createMoveMarker();

        // Create edge marker
        this._createResizeMarker();
    },

    _createMoveMarker: function () {
        // Children override
    },

    _createResizeMarker: function () {
        // Children override
    },

    _createMarker: function (latlng, icon) {
        var marker = new L.Marker(latlng, {
            draggable: true,
            icon: icon,
            zIndexOffset: 10
        });

        this._bindMarker(marker);

        this._markerGroup.addLayer(marker);

        return marker;
    },

    _bindMarker: function (marker) {
        marker
            .on('dragstart', this._onMarkerDragStart, this)
            .on('drag', this._onMarkerDrag, this)
            .on('dragend', this._onMarkerDragEnd, this);
    },

    _unbindMarker: function (marker) {
        marker
            .off('dragstart', this._onMarkerDragStart, this)
            .off('drag', this._onMarkerDrag, this)
            .off('dragend', this._onMarkerDragEnd, this);
    },

    _onMarkerDragStart: function (e) {
        var marker = e.target;
        marker.setOpacity(0);

        this._shape.fire('editstart');
    },

    _fireEdit: function () {
        this._shape.edited = true;
        this._shape.fire('edit');
    },

    _onMarkerDrag: function (e) {
        var marker = e.target,
            latlng = marker.getLatLng();

        if (marker === this._moveMarker) {
            this._move(latlng);
        } else {
            this._resize(latlng);
        }

        this._shape.redraw();
    },

    _onMarkerDragEnd: function (e) {
        var marker = e.target;
        marker.setOpacity(1);

        this._fireEdit();
    },

    _move: function () {
        // Children override
    },

    _resize: function () {
        // Children override
    }
});


L.Edit = L.Edit || {};

L.Edit.Rectangle = L.Edit.SimpleShape.extend({
    _createMoveMarker: function () {
        var bounds = this._shape.getBounds(),
            center = bounds.getCenter();

        this._moveMarker = this._createMarker(center, this.options.moveIcon);
    },

    _createResizeMarker: function () {
        var corners = this._getCorners();

        this._resizeMarkers = [];

        for (var i = 0, l = corners.length; i < l; i++) {
            this._resizeMarkers.push(this._createMarker(corners[i], this.options.resizeIcon));
            // Monkey in the corner index as we will need to know this for dragging
            this._resizeMarkers[i]._cornerIndex = i;
        }
    },

    _onMarkerDragStart: function (e) {
        L.Edit.SimpleShape.prototype._onMarkerDragStart.call(this, e);

        // Save a reference to the opposite point
        var corners = this._getCorners(),
            marker = e.target,
            currentCornerIndex = marker._cornerIndex;

        this._oppositeCorner = corners[(currentCornerIndex + 2) % 4];

        this._toggleCornerMarkers(0, currentCornerIndex);
    },

    _onMarkerDragEnd: function (e) {
        var marker = e.target,
            bounds, center;

        // Reset move marker position to the center
        if (marker === this._moveMarker) {
            bounds = this._shape.getBounds();
            center = bounds.getCenter();

            marker.setLatLng(center);
        }

        this._toggleCornerMarkers(1);

        this._repositionCornerMarkers();

        L.Edit.SimpleShape.prototype._onMarkerDragEnd.call(this, e);
    },

    _move: function (newCenter) {
        var latlngs = this._shape.getLatLngs(),
            bounds = this._shape.getBounds(),
            center = bounds.getCenter(),
            offset, newLatLngs = [];

        // Offset the latlngs to the new center
        for (var i = 0, l = latlngs.length; i < l; i++) {
            offset = [latlngs[i].lat - center.lat, latlngs[i].lng - center.lng];
            newLatLngs.push([newCenter.lat + offset[0], newCenter.lng + offset[1]]);
        }

        this._shape.setLatLngs(newLatLngs);

        // Reposition the resize markers
        this._repositionCornerMarkers();
    },

    _resize: function (latlng) {
        var bounds;

        // Update the shape based on the current position of this corner and the opposite point
        this._shape.setBounds(L.latLngBounds(latlng, this._oppositeCorner));

        // Reposition the move marker
        bounds = this._shape.getBounds();
        this._moveMarker.setLatLng(bounds.getCenter());
    },

    _getCorners: function () {
        var bounds = this._shape.getBounds(),
            nw = bounds.getNorthWest(),
            ne = bounds.getNorthEast(),
            se = bounds.getSouthEast(),
            sw = bounds.getSouthWest();

        return [nw, ne, se, sw];
    },

    _toggleCornerMarkers: function (opacity) {
        for (var i = 0, l = this._resizeMarkers.length; i < l; i++) {
            this._resizeMarkers[i].setOpacity(opacity);
        }
    },

    _repositionCornerMarkers: function () {
        var corners = this._getCorners();

        for (var i = 0, l = this._resizeMarkers.length; i < l; i++) {
            this._resizeMarkers[i].setLatLng(corners[i]);
        }
    }
});

L.Rectangle.addInitHook(function () {
    if (L.Edit.Rectangle) {
        this.editing = new L.Edit.Rectangle(this);

        if (this.options.editable) {
            this.editing.enable();
        }
    }
});


L.Edit = L.Edit || {};

L.Edit.Circle = L.Edit.SimpleShape.extend({
    _createMoveMarker: function () {
        var center = this._shape.getLatLng();

        this._moveMarker = this._createMarker(center, this.options.moveIcon);
    },

    _createResizeMarker: function () {
        var center = this._shape.getLatLng(),
            resizemarkerPoint = this._getResizeMarkerPoint(center);

        this._resizeMarkers = [];
        this._resizeMarkers.push(this._createMarker(resizemarkerPoint, this.options.resizeIcon));
    },

    _getResizeMarkerPoint: function (latlng) {
        // From L.shape.getBounds()
        var delta = this._shape._radius * Math.cos(Math.PI / 4),
            point = this._map.project(latlng);
        return this._map.unproject([point.x + delta, point.y - delta]);
    },

    _move: function (latlng) {
        var resizemarkerPoint = this._getResizeMarkerPoint(latlng);

        // Move the resize marker
        this._resizeMarkers[0].setLatLng(resizemarkerPoint);

        // Move the circle
        this._shape.setLatLng(latlng);
    },

    _resize: function (latlng) {
        var moveLatLng = this._moveMarker.getLatLng(),
            radius = moveLatLng.distanceTo(latlng);

        this._shape.setRadius(radius);
    }
});

L.Circle.addInitHook(function () {
    if (L.Edit.Circle) {
        this.editing = new L.Edit.Circle(this);

        if (this.options.editable) {
            this.editing.enable();
        }
    }

    this.on('add', function () {
        if (this.editing && this.editing.enabled()) {
            this.editing.addHooks();
        }
    });

    this.on('remove', function () {
        if (this.editing && this.editing.enabled()) {
            this.editing.removeHooks();
        }
    });
});

/*
 * L.LatLngUtil contains different utility functions for LatLngs.
 */

L.LatLngUtil = {
    // Clones a LatLngs[], returns [][]
    cloneLatLngs: function (latlngs) {
        var clone = [];
        for (var i = 0, l = latlngs.length; i < l; i++) {
            clone.push(this.cloneLatLng(latlngs[i]));
        }
        return clone;
    },

    cloneLatLng: function (latlng) {
        return L.latLng(latlng.lat, latlng.lng);
    }
};

L.GeometryUtil = L.extend(L.GeometryUtil || {}, {
    // Ported from the OpenLayers implementation. See https://github.com/openlayers/openlayers/blob/master/lib/OpenLayers/Geometry/LinearRing.js#L270
    geodesicArea: function (latLngs) {
        var pointsCount = latLngs.length,
            area = 0.0,
            d2r = L.LatLng.DEG_TO_RAD,
            p1, p2;

        if (pointsCount > 2) {
            for (var i = 0; i < pointsCount; i++) {
                p1 = latLngs[i];
                p2 = latLngs[(i + 1) % pointsCount];
                area += ((p2.lng - p1.lng) * d2r) *
                        (2 + Math.sin(p1.lat * d2r) + Math.sin(p2.lat * d2r));
            }
            area = area * 6378137.0 * 6378137.0 / 2.0;
        }

        return Math.abs(area);
    },

    readableArea: function (area, isMetric) {
        var areaStr;

        if (isMetric) {
            if (area >= 1000000) {
                areaStr = (area * 0.000001).toFixed(2) + ' km&sup2;';
            } else {
                areaStr = area.toFixed(2) + ' m&sup2;';
            }
        } else {
            area *= 0.836127; // Square yards in 1 meter

            if (area >= 3097600) { //3097600 square yards in 1 square mile
                areaStr = (area / 3097600).toFixed(2) + ' mi&sup2;';
            } else if (area >= 4840) {//48040 square yards in 1 acre
                areaStr = (area / 4840).toFixed(2) + ' acres';
            } else {
                areaStr = Math.ceil(area) + ' yd&sup2;';
            }
        }

        return areaStr;
    },

    readableDistance: function (distance, isMetric) {
        var distanceStr;

        if (isMetric) {
            // show metres when distance is < 1km, then show km
            if (distance > 1000) {
                distanceStr = (distance  / 1000).toFixed(2) + ' km';
            } else {
                distanceStr = Math.ceil(distance) + ' m';
            }
        } else {
            distance *= 1.09361;

            if (distance > 1760) {
                distanceStr = (distance / 1760).toFixed(2) + ' miles';
            } else {
                distanceStr = Math.ceil(distance) + ' yd';
            }
        }

        return distanceStr;
    }
});

L.Util.extend(L.LineUtil, {
    // Checks to see if two line segments intersect. Does not handle degenerate cases.
    // http://compgeom.cs.uiuc.edu/~jeffe/teaching/373/notes/x06-sweepline.pdf
    segmentsIntersect: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2, /*Point*/ p3) {
        return  this._checkCounterclockwise(p, p2, p3) !==
                this._checkCounterclockwise(p1, p2, p3) &&
                this._checkCounterclockwise(p, p1, p2) !==
                this._checkCounterclockwise(p, p1, p3);
    },

    // check to see if points are in counterclockwise order
    _checkCounterclockwise: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) {
        return (p2.y - p.y) * (p1.x - p.x) > (p1.y - p.y) * (p2.x - p.x);
    }
});

L.Polyline.include({
    // Check to see if this polyline has any linesegments that intersect.
    // NOTE: does not support detecting intersection for degenerate cases.
    intersects: function () {
        var points = this._originalPoints,
            len = points ? points.length : 0,
            i, p, p1;

        if (this._tooFewPointsForIntersection()) {
            return false;
        }

        for (i = len - 1; i >= 3; i--) {
            p = points[i - 1];
            p1 = points[i];


            if (this._lineSegmentsIntersectsRange(p, p1, i - 2)) {
                return true;
            }
        }

        return false;
    },

    // Check for intersection if new latlng was added to this polyline.
    // NOTE: does not support detecting intersection for degenerate cases.
    newLatLngIntersects: function (latlng, skipFirst) {
        // Cannot check a polyline for intersecting lats/lngs when not added to the map
        if (!this._map) {
            return false;
        }

        return this.newPointIntersects(this._map.latLngToLayerPoint(latlng), skipFirst);
    },

    // Check for intersection if new point was added to this polyline.
    // newPoint must be a layer point.
    // NOTE: does not support detecting intersection for degenerate cases.
    newPointIntersects: function (newPoint, skipFirst) {
        var points = this._originalPoints,
            len = points ? points.length : 0,
            lastPoint = points ? points[len - 1] : null,
            // The previous previous line segment. Previous line segment doesn't need testing.
            maxIndex = len - 2;

        if (this._tooFewPointsForIntersection(1)) {
            return false;
        }

        return this._lineSegmentsIntersectsRange(lastPoint, newPoint, maxIndex, skipFirst ? 1 : 0);
    },

    // Polylines with 2 sides can only intersect in cases where points are collinear (we don't support detecting these).
    // Cannot have intersection when < 3 line segments (< 4 points)
    _tooFewPointsForIntersection: function (extraPoints) {
        var points = this._originalPoints,
            len = points ? points.length : 0;
        // Increment length by extraPoints if present
        len += extraPoints || 0;

        return !this._originalPoints || len <= 3;
    },

    // Checks a line segment intersections with any line segments before its predecessor.
    // Don't need to check the predecessor as will never intersect.
    _lineSegmentsIntersectsRange: function (p, p1, maxIndex, minIndex) {
        var points = this._originalPoints,
            p2, p3;

        minIndex = minIndex || 0;

        // Check all previous line segments (beside the immediately previous) for intersections
        for (var j = maxIndex; j > minIndex; j--) {
            p2 = points[j - 1];
            p3 = points[j];

            if (L.LineUtil.segmentsIntersect(p, p1, p2, p3)) {
                return true;
            }
        }

        return false;
    }
});


L.Polygon.include({
    // Checks a polygon for any intersecting line segments. Ignores holes.
    intersects: function () {
        var polylineIntersects,
            points = this._originalPoints,
            len, firstPoint, lastPoint, maxIndex;

        if (this._tooFewPointsForIntersection()) {
            return false;
        }

        polylineIntersects = L.Polyline.prototype.intersects.call(this);

        // If already found an intersection don't need to check for any more.
        if (polylineIntersects) {
            return true;
        }

        len = points.length;
        firstPoint = points[0];
        lastPoint = points[len - 1];
        maxIndex = len - 2;

        // Check the line segment between last and first point. Don't need to check the first line segment (minIndex = 1)
        return this._lineSegmentsIntersectsRange(lastPoint, firstPoint, maxIndex, 1);
    }
});

L.Control.Draw = L.Control.extend({

    options: {
        position: 'topleft',
        draw: {},
        edit: false
    },

    initialize: function (options) {
        if (L.version < '0.7') {
            throw new Error('Leaflet.draw 0.2.3+ requires Leaflet 0.7.0+. Download latest from https://github.com/Leaflet/Leaflet/');
        }

        L.Control.prototype.initialize.call(this, options);

        var toolbar;

        this._toolbars = {};

        // Initialize toolbars
        if (L.DrawToolbar && this.options.draw) {
            toolbar = new L.DrawToolbar(this.options.draw);

            this._toolbars[L.DrawToolbar.TYPE] = toolbar;

            // Listen for when toolbar is enabled
            this._toolbars[L.DrawToolbar.TYPE].on('enable', this._toolbarEnabled, this);
        }

        if (L.EditToolbar && this.options.edit) {
            toolbar = new L.EditToolbar(this.options.edit);

            this._toolbars[L.EditToolbar.TYPE] = toolbar;

            // Listen for when toolbar is enabled
            this._toolbars[L.EditToolbar.TYPE].on('enable', this._toolbarEnabled, this);
        }
    },

    onAdd: function (map) {
        var container = L.DomUtil.create('div', 'leaflet-draw'),
            addedTopClass = false,
            topClassName = 'leaflet-draw-toolbar-top',
            toolbarContainer;

        for (var toolbarId in this._toolbars) {
            if (this._toolbars.hasOwnProperty(toolbarId)) {
                toolbarContainer = this._toolbars[toolbarId].addToolbar(map);

                if (toolbarContainer) {
                    // Add class to the first toolbar to remove the margin
                    if (!addedTopClass) {
                        if (!L.DomUtil.hasClass(toolbarContainer, topClassName)) {
                            L.DomUtil.addClass(toolbarContainer.childNodes[0], topClassName);
                        }
                        addedTopClass = true;
                    }

                    container.appendChild(toolbarContainer);
                }
            }
        }

        return container;
    },

    onRemove: function () {
        for (var toolbarId in this._toolbars) {
            if (this._toolbars.hasOwnProperty(toolbarId)) {
                this._toolbars[toolbarId].removeToolbar();
            }
        }
    },

    setDrawingOptions: function (options) {
        for (var toolbarId in this._toolbars) {
            if (this._toolbars[toolbarId] instanceof L.DrawToolbar) {
                this._toolbars[toolbarId].setOptions(options);
            }
        }
    },

    _toolbarEnabled: function (e) {
        var enabledToolbar = e.target;

        for (var toolbarId in this._toolbars) {
            if (this._toolbars[toolbarId] !== enabledToolbar) {
                this._toolbars[toolbarId].disable();
            }
        }
    }
});

L.Map.mergeOptions({
    drawControlTooltips: true,
    drawControl: false
});

L.Map.addInitHook(function () {
    if (this.options.drawControl) {
        this.drawControl = new L.Control.Draw();
        this.addControl(this.drawControl);
    }
});


L.Toolbar = L.Class.extend({
    includes: [L.Mixin.Events],

    initialize: function (options) {
        L.setOptions(this, options);

        this._modes = {};
        this._actionButtons = [];
        this._activeMode = null;
    },

    enabled: function () {
        return this._activeMode !== null;
    },

    disable: function () {
        if (!this.enabled()) { return; }

        this._activeMode.handler.disable();
    },

    addToolbar: function (map) {
        var container = L.DomUtil.create('div', 'leaflet-draw-section'),
            buttonIndex = 0,
            buttonClassPrefix = this._toolbarClass || '',
            modeHandlers = this.getModeHandlers(map),
            i;

        this._toolbarContainer = L.DomUtil.create('div', 'leaflet-draw-toolbar leaflet-bar');
        this._map = map;

        for (i = 0; i < modeHandlers.length; i++) {
            if (modeHandlers[i].enabled) {
                this._initModeHandler(
                    modeHandlers[i].handler,
                    this._toolbarContainer,
                    buttonIndex++,
                    buttonClassPrefix,
                    modeHandlers[i].title
                );
            }
        }

        // if no buttons were added, do not add the toolbar
        if (!buttonIndex) {
            return;
        }

        // Save button index of the last button, -1 as we would have ++ after the last button
        this._lastButtonIndex = --buttonIndex;

        // Create empty actions part of the toolbar
        this._actionsContainer = L.DomUtil.create('ul', 'leaflet-draw-actions');

        // Add draw and cancel containers to the control container
        container.appendChild(this._toolbarContainer);
        container.appendChild(this._actionsContainer);

        return container;
    },

    removeToolbar: function () {
        // Dispose each handler
        for (var handlerId in this._modes) {
            if (this._modes.hasOwnProperty(handlerId)) {
                // Unbind handler button
                this._disposeButton(
                    this._modes[handlerId].button,
                    this._modes[handlerId].handler.enable,
                    this._modes[handlerId].handler
                );

                // Make sure is disabled
                this._modes[handlerId].handler.disable();

                // Unbind handler
                this._modes[handlerId].handler
                    .off('enabled', this._handlerActivated, this)
                    .off('disabled', this._handlerDeactivated, this);
            }
        }
        this._modes = {};

        // Dispose the actions toolbar
        for (var i = 0, l = this._actionButtons.length; i < l; i++) {
            this._disposeButton(
                this._actionButtons[i].button,
                this._actionButtons[i].callback,
                this
            );
        }
        this._actionButtons = [];
        this._actionsContainer = null;
    },

    _initModeHandler: function (handler, container, buttonIndex, classNamePredix, buttonTitle) {
        var type = handler.type;

        this._modes[type] = {};

        this._modes[type].handler = handler;

        this._modes[type].button = this._createButton({
            title: buttonTitle,
            className: classNamePredix + '-' + type,
            container: container,
            callback: this._modes[type].handler.enable,
            context: this._modes[type].handler
        });

        this._modes[type].buttonIndex = buttonIndex;

        this._modes[type].handler
            .on('enabled', this._handlerActivated, this)
            .on('disabled', this._handlerDeactivated, this);
    },

    _createButton: function (options) {
        var link = L.DomUtil.create('a', options.className || '', options.container);
        link.href = '#';

        if (options.text) {
            link.innerHTML = options.text;
        }

        if (options.title) {
            link.title = options.title;
        }

        L.DomEvent
            .on(link, 'click', L.DomEvent.stopPropagation)
            .on(link, 'mousedown', L.DomEvent.stopPropagation)
            .on(link, 'dblclick', L.DomEvent.stopPropagation)
            .on(link, 'click', L.DomEvent.preventDefault)
            .on(link, 'click', options.callback, options.context);

        return link;
    },

    _disposeButton: function (button, callback) {
        L.DomEvent
            .off(button, 'click', L.DomEvent.stopPropagation)
            .off(button, 'mousedown', L.DomEvent.stopPropagation)
            .off(button, 'dblclick', L.DomEvent.stopPropagation)
            .off(button, 'click', L.DomEvent.preventDefault)
            .off(button, 'click', callback);
    },

    _handlerActivated: function (e) {
        // Disable active mode (if present)
        this.disable();

        // Cache new active feature
        this._activeMode = this._modes[e.handler];

        L.DomUtil.addClass(this._activeMode.button, 'leaflet-draw-toolbar-button-enabled');

        this._showActionsToolbar();

        this.fire('enable');
    },

    _handlerDeactivated: function () {
        this._hideActionsToolbar();

        L.DomUtil.removeClass(this._activeMode.button, 'leaflet-draw-toolbar-button-enabled');

        this._activeMode = null;

        this.fire('disable');
    },

    _createActions: function (handler) {
        var container = this._actionsContainer,
            buttons = this.getActions(handler),
            l = buttons.length,
            li, di, dl, button;

        // Dispose the actions toolbar (todo: dispose only not used buttons)
        for (di = 0, dl = this._actionButtons.length; di < dl; di++) {
            this._disposeButton(this._actionButtons[di].button, this._actionButtons[di].callback);
        }
        this._actionButtons = [];

        // Remove all old buttons
        while (container.firstChild) {
            container.removeChild(container.firstChild);
        }

        for (var i = 0; i < l; i++) {
            if ('enabled' in buttons[i] && !buttons[i].enabled) {
                continue;
            }

            li = L.DomUtil.create('li', '', container);

            button = this._createButton({
                title: buttons[i].title,
                text: buttons[i].text,
                container: li,
                callback: buttons[i].callback,
                context: buttons[i].context
            });

            this._actionButtons.push({
                button: button,
                callback: buttons[i].callback
            });
        }
    },

    _showActionsToolbar: function () {
        var buttonIndex = this._activeMode.buttonIndex,
            lastButtonIndex = this._lastButtonIndex,
            toolbarPosition = this._activeMode.button.offsetTop - 1;

        // Recreate action buttons on every click
        this._createActions(this._activeMode.handler);

        // Correctly position the cancel button
        this._actionsContainer.style.top = toolbarPosition + 'px';

        if (buttonIndex === 0) {
            L.DomUtil.addClass(this._toolbarContainer, 'leaflet-draw-toolbar-notop');
            L.DomUtil.addClass(this._actionsContainer, 'leaflet-draw-actions-top');
        }

        if (buttonIndex === lastButtonIndex) {
            L.DomUtil.addClass(this._toolbarContainer, 'leaflet-draw-toolbar-nobottom');
            L.DomUtil.addClass(this._actionsContainer, 'leaflet-draw-actions-bottom');
        }

        this._actionsContainer.style.display = 'block';
    },

    _hideActionsToolbar: function () {
        this._actionsContainer.style.display = 'none';

        L.DomUtil.removeClass(this._toolbarContainer, 'leaflet-draw-toolbar-notop');
        L.DomUtil.removeClass(this._toolbarContainer, 'leaflet-draw-toolbar-nobottom');
        L.DomUtil.removeClass(this._actionsContainer, 'leaflet-draw-actions-top');
        L.DomUtil.removeClass(this._actionsContainer, 'leaflet-draw-actions-bottom');
    }
});


L.Tooltip = L.Class.extend({
    initialize: function (map) {
        this._map = map;
        this._popupPane = map._panes.popupPane;

        this._container = map.options.drawControlTooltips ? L.DomUtil.create('div', 'leaflet-draw-tooltip', this._popupPane) : null;
        this._singleLineLabel = false;
    },

    dispose: function () {
        if (this._container) {
            this._popupPane.removeChild(this._container);
            this._container = null;
        }
    },

    updateContent: function (labelText) {
        if (!this._container) {
            return this;
        }
        labelText.subtext = labelText.subtext || '';

        // update the vertical position (only if changed)
        if (labelText.subtext.length === 0 && !this._singleLineLabel) {
            L.DomUtil.addClass(this._container, 'leaflet-draw-tooltip-single');
            this._singleLineLabel = true;
        }
        else if (labelText.subtext.length > 0 && this._singleLineLabel) {
            L.DomUtil.removeClass(this._container, 'leaflet-draw-tooltip-single');
            this._singleLineLabel = false;
        }

        this._container.innerHTML =
            (labelText.subtext.length > 0 ? '<span class="leaflet-draw-tooltip-subtext">' + labelText.subtext + '</span>' + '<br />' : '') +
            '<span>' + labelText.text + '</span>';

        return this;
    },

    updatePosition: function (latlng) {
        var pos = this._map.latLngToLayerPoint(latlng),
            tooltipContainer = this._container;

        if (this._container) {
            tooltipContainer.style.visibility = 'inherit';
            L.DomUtil.setPosition(tooltipContainer, pos);
        }

        return this;
    },

    showAsError: function () {
        if (this._container) {
            L.DomUtil.addClass(this._container, 'leaflet-error-draw-tooltip');
        }
        return this;
    },

    removeError: function () {
        if (this._container) {
            L.DomUtil.removeClass(this._container, 'leaflet-error-draw-tooltip');
        }
        return this;
    }
});

L.DrawToolbar = L.Toolbar.extend({

    statics: {
        TYPE: 'draw'
    },

    options: {
        polyline: {},
        polygon: {},
        rectangle: {},
        circle: {},
        marker: {}
    },

    initialize: function (options) {
        // Ensure that the options are merged correctly since L.extend is only shallow
        for (var type in this.options) {
            if (this.options.hasOwnProperty(type)) {
                if (options[type]) {
                    options[type] = L.extend({}, this.options[type], options[type]);
                }
            }
        }

        this._toolbarClass = 'leaflet-draw-draw';
        L.Toolbar.prototype.initialize.call(this, options);
    },

    getModeHandlers: function (map) {
        return [
            {
                enabled: this.options.polyline,
                handler: new L.Draw.Polyline(map, this.options.polyline),
                title: L.drawLocal.draw.toolbar.buttons.polyline
            },
            {
                enabled: this.options.polygon,
                handler: new L.Draw.Polygon(map, this.options.polygon),
                title: L.drawLocal.draw.toolbar.buttons.polygon
            },
            {
                enabled: this.options.rectangle,
                handler: new L.Draw.Rectangle(map, this.options.rectangle),
                title: L.drawLocal.draw.toolbar.buttons.rectangle
            },
            {
                enabled: this.options.circle,
                handler: new L.Draw.Circle(map, this.options.circle),
                title: L.drawLocal.draw.toolbar.buttons.circle
            },
            {
                enabled: this.options.marker,
                handler: new L.Draw.Marker(map, this.options.marker),
                title: L.drawLocal.draw.toolbar.buttons.marker
            }
        ];
    },

    // Get the actions part of the toolbar
    getActions: function (handler) {
        return [
            {
                enabled: handler.deleteLastVertex,
                title: L.drawLocal.draw.toolbar.undo.title,
                text: L.drawLocal.draw.toolbar.undo.text,
                callback: handler.deleteLastVertex,
                context: handler
            },
            {
                title: L.drawLocal.draw.toolbar.actions.title,
                text: L.drawLocal.draw.toolbar.actions.text,
                callback: this.disable,
                context: this
            }
        ];
    },

    setOptions: function (options) {
        L.setOptions(this, options);

        for (var type in this._modes) {
            if (this._modes.hasOwnProperty(type) && options.hasOwnProperty(type)) {
                this._modes[type].handler.setOptions(options[type]);
            }
        }
    }
});


/*L.Map.mergeOptions({
    editControl: true
});*/

L.EditToolbar = L.Toolbar.extend({
    statics: {
        TYPE: 'edit'
    },

    options: {
        edit: {
            selectedPathOptions: {
                color: '#fe57a1', /* Hot pink all the things! */
                opacity: 0.6,
                dashArray: '10, 10',

                fill: true,
                fillColor: '#fe57a1',
                fillOpacity: 0.1,

                // Whether to user the existing layers color
                maintainColor: false
            }
        },
        remove: {},
        featureGroup: null /* REQUIRED! TODO: perhaps if not set then all layers on the map are selectable? */
    },

    initialize: function (options) {
        // Need to set this manually since null is an acceptable value here
        if (options.edit) {
            if (typeof options.edit.selectedPathOptions === 'undefined') {
                options.edit.selectedPathOptions = this.options.edit.selectedPathOptions;
            }
            options.edit.selectedPathOptions = L.extend({}, this.options.edit.selectedPathOptions, options.edit.selectedPathOptions);
        }

        if (options.remove) {
            options.remove = L.extend({}, this.options.remove, options.remove);
        }

        this._toolbarClass = 'leaflet-draw-edit';
        L.Toolbar.prototype.initialize.call(this, options);

        this._selectedFeatureCount = 0;
    },

    getModeHandlers: function (map) {
        var featureGroup = this.options.featureGroup;
        return [
            {
                enabled: this.options.edit,
                handler: new L.EditToolbar.Edit(map, {
                    featureGroup: featureGroup,
                    selectedPathOptions: this.options.edit.selectedPathOptions
                }),
                title: L.drawLocal.edit.toolbar.buttons.edit
            },
            {
                enabled: this.options.remove,
                handler: new L.EditToolbar.Delete(map, {
                    featureGroup: featureGroup
                }),
                title: L.drawLocal.edit.toolbar.buttons.remove
            }
        ];
    },

    getActions: function () {
        return [
            {
                title: L.drawLocal.edit.toolbar.actions.save.title,
                text: L.drawLocal.edit.toolbar.actions.save.text,
                callback: this._save,
                context: this
            },
            {
                title: L.drawLocal.edit.toolbar.actions.cancel.title,
                text: L.drawLocal.edit.toolbar.actions.cancel.text,
                callback: this.disable,
                context: this
            }
        ];
    },

    addToolbar: function (map) {
        var container = L.Toolbar.prototype.addToolbar.call(this, map);

        this._checkDisabled();

        this.options.featureGroup.on('layeradd layerremove', this._checkDisabled, this);

        return container;
    },

    removeToolbar: function () {
        this.options.featureGroup.off('layeradd layerremove', this._checkDisabled, this);

        L.Toolbar.prototype.removeToolbar.call(this);
    },

    disable: function () {
        if (!this.enabled()) { return; }

        this._activeMode.handler.revertLayers();

        L.Toolbar.prototype.disable.call(this);
    },

    _save: function () {
        this._activeMode.handler.save();
        this._activeMode.handler.disable();
    },

    _checkDisabled: function () {
        var featureGroup = this.options.featureGroup,
            hasLayers = featureGroup.getLayers().length !== 0,
            button;

        if (this.options.edit) {
            button = this._modes[L.EditToolbar.Edit.TYPE].button;

            if (hasLayers) {
                L.DomUtil.removeClass(button, 'leaflet-disabled');
            } else {
                L.DomUtil.addClass(button, 'leaflet-disabled');
            }

            button.setAttribute(
                'title',
                hasLayers ?
                L.drawLocal.edit.toolbar.buttons.edit
                : L.drawLocal.edit.toolbar.buttons.editDisabled
            );
        }

        if (this.options.remove) {
            button = this._modes[L.EditToolbar.Delete.TYPE].button;

            if (hasLayers) {
                L.DomUtil.removeClass(button, 'leaflet-disabled');
            } else {
                L.DomUtil.addClass(button, 'leaflet-disabled');
            }

            button.setAttribute(
                'title',
                hasLayers ?
                L.drawLocal.edit.toolbar.buttons.remove
                : L.drawLocal.edit.toolbar.buttons.removeDisabled
            );
        }
    }
});


L.EditToolbar.Edit = L.Handler.extend({
    statics: {
        TYPE: 'edit'
    },

    includes: L.Mixin.Events,

    initialize: function (map, options) {
        L.Handler.prototype.initialize.call(this, map);

        // Set options to the default unless already set
        this._selectedPathOptions = options.selectedPathOptions;

        // Store the selectable layer group for ease of access
        this._featureGroup = options.featureGroup;

        if (!(this._featureGroup instanceof L.FeatureGroup)) {
            throw new Error('options.featureGroup must be a L.FeatureGroup');
        }

        this._uneditedLayerProps = {};

        // Save the type so super can fire, need to do this as cannot do this.TYPE :(
        this.type = L.EditToolbar.Edit.TYPE;
    },

    enable: function () {
        if (this._enabled || !this._hasAvailableLayers()) {
            return;
        }
        this.fire('enabled', {handler: this.type});
            //this disable other handlers

        this._map.fire('draw:editstart', { handler: this.type });
            //allow drawLayer to be updated before beginning edition.

        L.Handler.prototype.enable.call(this);
        this._featureGroup
            .on('layeradd', this._enableLayerEdit, this)
            .on('layerremove', this._disableLayerEdit, this);
    },

    disable: function () {
        if (!this._enabled) { return; }
        this._featureGroup
            .off('layeradd', this._enableLayerEdit, this)
            .off('layerremove', this._disableLayerEdit, this);
        L.Handler.prototype.disable.call(this);
        this._map.fire('draw:editstop', { handler: this.type });
        this.fire('disabled', {handler: this.type});
    },

    addHooks: function () {
        var map = this._map;

        if (map) {
            map.getContainer().focus();

            this._featureGroup.eachLayer(this._enableLayerEdit, this);

            this._tooltip = new L.Tooltip(this._map);
            this._tooltip.updateContent({
                text: L.drawLocal.edit.handlers.edit.tooltip.text,
                subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext
            });

            this._map.on('mousemove', this._onMouseMove, this);
        }
    },

    removeHooks: function () {
        if (this._map) {
            // Clean up selected layers.
            this._featureGroup.eachLayer(this._disableLayerEdit, this);

            // Clear the backups of the original layers
            this._uneditedLayerProps = {};

            this._tooltip.dispose();
            this._tooltip = null;

            this._map.off('mousemove', this._onMouseMove, this);
        }
    },

    revertLayers: function () {
        this._featureGroup.eachLayer(function (layer) {
            this._revertLayer(layer);
        }, this);
    },

    save: function () {
        var editedLayers = new L.LayerGroup();
        this._featureGroup.eachLayer(function (layer) {
            if (layer.edited) {
                editedLayers.addLayer(layer);
                layer.edited = false;
            }
        });
        this._map.fire('draw:edited', {layers: editedLayers});
    },

    _backupLayer: function (layer) {
        var id = L.Util.stamp(layer);

        if (!this._uneditedLayerProps[id]) {
            // Polyline, Polygon or Rectangle
            if (layer instanceof L.Polyline || layer instanceof L.Polygon || layer instanceof L.Rectangle) {
                this._uneditedLayerProps[id] = {
                    latlngs: L.LatLngUtil.cloneLatLngs(layer.getLatLngs())
                };
            } else if (layer instanceof L.Circle) {
                this._uneditedLayerProps[id] = {
                    latlng: L.LatLngUtil.cloneLatLng(layer.getLatLng()),
                    radius: layer.getRadius()
                };
            } else if (layer instanceof L.Marker) { // Marker
                this._uneditedLayerProps[id] = {
                    latlng: L.LatLngUtil.cloneLatLng(layer.getLatLng())
                };
            }
        }
    },

    _revertLayer: function (layer) {
        var id = L.Util.stamp(layer);
        layer.edited = false;
        if (this._uneditedLayerProps.hasOwnProperty(id)) {
            // Polyline, Polygon or Rectangle
            if (layer instanceof L.Polyline || layer instanceof L.Polygon || layer instanceof L.Rectangle) {
                layer.setLatLngs(this._uneditedLayerProps[id].latlngs);
            } else if (layer instanceof L.Circle) {
                layer.setLatLng(this._uneditedLayerProps[id].latlng);
                layer.setRadius(this._uneditedLayerProps[id].radius);
            } else if (layer instanceof L.Marker) { // Marker
                layer.setLatLng(this._uneditedLayerProps[id].latlng);
            }
        }
    },

    _toggleMarkerHighlight: function (marker) {
        if (!marker._icon) {
            return;
        }
        // This is quite naughty, but I don't see another way of doing it. (short of setting a new icon)
        var icon = marker._icon;

        icon.style.display = 'none';

        if (L.DomUtil.hasClass(icon, 'leaflet-edit-marker-selected')) {
            L.DomUtil.removeClass(icon, 'leaflet-edit-marker-selected');
            // Offset as the border will make the icon move.
            this._offsetMarker(icon, -4);

        } else {
            L.DomUtil.addClass(icon, 'leaflet-edit-marker-selected');
            // Offset as the border will make the icon move.
            this._offsetMarker(icon, 4);
        }

        icon.style.display = '';
    },

    _offsetMarker: function (icon, offset) {
        var iconMarginTop = parseInt(icon.style.marginTop, 10) - offset,
            iconMarginLeft = parseInt(icon.style.marginLeft, 10) - offset;

        icon.style.marginTop = iconMarginTop + 'px';
        icon.style.marginLeft = iconMarginLeft + 'px';
    },

    _enableLayerEdit: function (e) {
        var layer = e.layer || e.target || e,
            isMarker = layer instanceof L.Marker,
            pathOptions;

        // Don't do anything if this layer is a marker but doesn't have an icon. Markers
        // should usually have icons. If using Leaflet.draw with Leafler.markercluster there
        // is a chance that a marker doesn't.
        if (isMarker && !layer._icon) {
            return;
        }

        // Back up this layer (if haven't before)
        this._backupLayer(layer);

        // Update layer style so appears editable
        if (this._selectedPathOptions) {
            pathOptions = L.Util.extend({}, this._selectedPathOptions);

            // Use the existing color of the layer
            if (pathOptions.maintainColor) {
                pathOptions.color = layer.options.color;
                pathOptions.fillColor = layer.options.fillColor;
            }

            if (isMarker) {
                this._toggleMarkerHighlight(layer);
            } else {
                layer.options.previousOptions = L.Util.extend({ dashArray: null }, layer.options);

                // Make sure that Polylines are not filled
                if (!(layer instanceof L.Circle) && !(layer instanceof L.Polygon) && !(layer instanceof L.Rectangle)) {
                    pathOptions.fill = false;
                }

                layer.setStyle(pathOptions);
            }
        }

        if (isMarker) {
            layer.dragging.enable();
            layer.on('dragend', this._onMarkerDragEnd);
        } else {
            layer.editing.enable();
        }
    },

    _disableLayerEdit: function (e) {
        var layer = e.layer || e.target || e;
        layer.edited = false;

        // Reset layer styles to that of before select
        if (this._selectedPathOptions) {
            if (layer instanceof L.Marker) {
                this._toggleMarkerHighlight(layer);
            } else {
                // reset the layer style to what is was before being selected
                layer.setStyle(layer.options.previousOptions);
                // remove the cached options for the layer object
                delete layer.options.previousOptions;
            }
        }

        if (layer instanceof L.Marker) {
            layer.dragging.disable();
            layer.off('dragend', this._onMarkerDragEnd, this);
        } else {
            layer.editing.disable();
        }
    },

    _onMarkerDragEnd: function (e) {
        var layer = e.target;
        layer.edited = true;
    },

    _onMouseMove: function (e) {
        this._tooltip.updatePosition(e.latlng);
    },

    _hasAvailableLayers: function () {
        return this._featureGroup.getLayers().length !== 0;
    }
});


L.EditToolbar.Delete = L.Handler.extend({
    statics: {
        TYPE: 'remove' // not delete as delete is reserved in js
    },

    includes: L.Mixin.Events,

    initialize: function (map, options) {
        L.Handler.prototype.initialize.call(this, map);

        L.Util.setOptions(this, options);

        // Store the selectable layer group for ease of access
        this._deletableLayers = this.options.featureGroup;

        if (!(this._deletableLayers instanceof L.FeatureGroup)) {
            throw new Error('options.featureGroup must be a L.FeatureGroup');
        }

        // Save the type so super can fire, need to do this as cannot do this.TYPE :(
        this.type = L.EditToolbar.Delete.TYPE;
    },

    enable: function () {
        if (this._enabled || !this._hasAvailableLayers()) {
            return;
        }
        this.fire('enabled', { handler: this.type});

        this._map.fire('draw:deletestart', { handler: this.type });

        L.Handler.prototype.enable.call(this);

        this._deletableLayers
            .on('layeradd', this._enableLayerDelete, this)
            .on('layerremove', this._disableLayerDelete, this);
    },

    disable: function () {
        if (!this._enabled) { return; }

        this._deletableLayers
            .off('layeradd', this._enableLayerDelete, this)
            .off('layerremove', this._disableLayerDelete, this);

        L.Handler.prototype.disable.call(this);

        this._map.fire('draw:deletestop', { handler: this.type });

        this.fire('disabled', { handler: this.type});
    },

    addHooks: function () {
        var map = this._map;

        if (map) {
            map.getContainer().focus();

            this._deletableLayers.eachLayer(this._enableLayerDelete, this);
            this._deletedLayers = new L.layerGroup();

            this._tooltip = new L.Tooltip(this._map);
            this._tooltip.updateContent({ text: L.drawLocal.edit.handlers.remove.tooltip.text });

            this._map.on('mousemove', this._onMouseMove, this);
        }
    },

    removeHooks: function () {
        if (this._map) {
            this._deletableLayers.eachLayer(this._disableLayerDelete, this);
            this._deletedLayers = null;

            this._tooltip.dispose();
            this._tooltip = null;

            this._map.off('mousemove', this._onMouseMove, this);
        }
    },

    revertLayers: function () {
        // Iterate of the deleted layers and add them back into the featureGroup
        this._deletedLayers.eachLayer(function (layer) {
            this._deletableLayers.addLayer(layer);
        }, this);
    },

    save: function () {
        this._map.fire('draw:deleted', { layers: this._deletedLayers });
    },

    _enableLayerDelete: function (e) {
        var layer = e.layer || e.target || e;

        layer.on('click', this._removeLayer, this);
    },

    _disableLayerDelete: function (e) {
        var layer = e.layer || e.target || e;

        layer.off('click', this._removeLayer, this);

        // Remove from the deleted layers so we can't accidently revert if the user presses cancel
        this._deletedLayers.removeLayer(layer);
    },

    _removeLayer: function (e) {
        var layer = e.layer || e.target || e;

        this._deletableLayers.removeLayer(layer);

        this._deletedLayers.addLayer(layer);
    },

    _onMouseMove: function (e) {
        this._tooltip.updatePosition(e.latlng);
    },

    _hasAvailableLayers: function () {
        return this._deletableLayers.getLayers().length !== 0;
    }
});


}(window, document));